package com.iw.core.utils;

import com.iw.core.orm.Compositor;
import com.iw.core.orm.Compositor.CompositorType;
import com.iw.core.orm.Filtration;
import com.iw.core.orm.Filtration.MatchType;
import com.iw.core.orm.PageData;
import com.iw.core.orm.hibernate.BaseEntity;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.*;
import org.hibernate.impl.CriteriaImpl;
import org.hibernate.transform.ResultTransformer;
import org.springframework.util.Assert;

import java.util.*;

/**
 * hibernate工具类
 */
public class HibernateUtils {
    /**
     * 根据Criterion条件创建Criteria.
     */
    public static Criteria createCriteria(Session session, Class<? extends BaseEntity> entityClass, Criterion... criterions) {
        Criteria criteria = session.createCriteria(entityClass);
        for (Criterion criterion : criterions) {
            criteria.add(criterion);
        }
        return criteria;
    }

    /**
     * 根据查询HQL与参数列表创建Query对象.
     */
    public static Query createQuery(Session session, String hql, Object... values) {
        Assert.hasText(hql, "hql不能为空");
        Query query = session.createQuery(hql);

        for (int i = 0; i < values.length; i++) {
            query.setParameter(i, values[i]);
        }

        return query;
    }

    /**
     * 根据查询HQL与参数列表创建Query对象.
     */
    public static Query createQuery(Session session, String hql, Map<String, ?> values) {
        Assert.hasText(hql, "hql不能为空");
        Query query = session.createQuery(hql);
        if (values != null) {
            query.setProperties(values);
        }
        return query;
    }

    /**
     * 创建Criterion
     */
    private static Criterion createCriterion(String fieldName, Object fieldValue, MatchType matchType) {
        Criterion criterion = null;
        Assert.hasText(fieldName, "fieldName不能为空");
        switch (matchType) {
            case EQ: // =
                criterion = Restrictions.eq(fieldName, fieldValue);
                break;
            case LIKE: // like
                criterion = Restrictions.like(fieldName, (String) fieldValue, MatchMode.ANYWHERE);
                break;
            case LT: // <
                criterion = Restrictions.lt(fieldName, fieldValue);
                break;
            case LE: // <=
                criterion = Restrictions.le(fieldName, fieldValue);
                break;
            case GT: // >
                criterion = Restrictions.gt(fieldName, fieldValue);
                break;
            case GE: // >=
                criterion = Restrictions.ge(fieldName, fieldValue);
                break;
        }
        return criterion;
    }

    /**
     * 执行count查询获得本次Criteria查询所能获得的对象总数.
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    private static long countCriteriaResult(Criteria criteria) {
        CriteriaImpl impl = (CriteriaImpl) criteria;

        // 先把Projection、ResultTransformer、OrderBy取出来,清空三者后再执行Count操作
        Projection projection = impl.getProjection();
        ResultTransformer resultTransformer = impl.getResultTransformer();

        List<CriteriaImpl.OrderEntry> orderEntries = null;
        orderEntries = (List) ReflectionUtils.getFieldValue(impl, "orderEntries");
        ReflectionUtils.setFieldValue(impl, "orderEntries", new ArrayList());

        // 执行Count查询
        long totalCount = 0;
        try {
            totalCount = Long.parseLong(criteria.setProjection(Projections.rowCount()).uniqueResult().toString());
        } catch (Exception e) {
        }

        // 将之前的Projection,ResultTransformer和OrderBy条件重新设回去

        criteria.setProjection(projection);// .setProjection(Projections.distinct(Projections.groupProperty("id,*")));

        if (projection == null) {
            criteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
        }
        if (resultTransformer != null) {
            criteria.setResultTransformer(resultTransformer);
        }
        // criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
        ReflectionUtils.setFieldValue(impl, "orderEntries", orderEntries);

        return totalCount;
    }

    /**
     * 设置排序参数到Criteria对象
     */
    public static Criteria setCompositorParameter(Criteria criteria, Compositor compositor) {
        if (compositor != null) {
            String fieldName = compositor.getFieldName();
            CompositorType compositorType = compositor.getCompositorType();
            switch (compositorType) {
                case ASC:
                    criteria.addOrder(Order.asc(fieldName));
                    break;
                case DESC:
                    criteria.addOrder(Order.desc(fieldName));
                    break;
            }
        }

        return criteria;
    }

    /**
     * 设置关联按了对象的属性查询
     *
     * @param criteria
     * @param names
     * @param value
     * @param matchType
     * @return
     */
    private static Criterion setQueryPropertity(Criteria criteria, String[] names, Object value, MatchType matchType, Set<String> aliasSet) {
        Criterion criterion = null;
        String aliasName = "T_ALIAS" + "_" + names[0].toUpperCase();
        if (!aliasSet.contains(aliasName)) {
            criteria.createAlias(names[0], aliasName);
        }
        aliasSet.add(aliasName);
        aliasName += ".";
        switch (matchType) {
            case EQ: // =
                criterion = Restrictions.eq(aliasName + names[1], value);
                break;
            case LIKE: // like
                criterion = Restrictions.like(aliasName + names[1], (String) value, MatchMode.ANYWHERE);
                break;
            case LT: // <
                criterion = Restrictions.lt(aliasName + names[1], value);
                break;
            case LE: // <=
                criterion = Restrictions.le(aliasName + names[1], value);
                break;
            case GT: // >
                criterion = Restrictions.gt(aliasName + names[1], value);
                break;
            case GE: // >=
                criterion = Restrictions.ge(aliasName + names[1], value);
                break;
        }

        return criterion;
    }

    /**
     * 设置过滤条件到Criteria对象
     */
    public static Criteria setFiltrationParameter(Criteria criteria, Filtration... filtrations) {
        Set<String> aliasSet = new HashSet<String>();
        if (filtrations.length > 0) {
            List<Criterion> criterions = new ArrayList<Criterion>();
            for (Filtration filtration : filtrations) {
                Criterion criterion = null;
                if (!filtration.isMultiFilter()) {
                    String fieldName = filtration.getFieldName();
                    Object v = filtration.getFieldValue();
                    if (fieldName.contains(".") && !(v instanceof Integer)) {
                        String[] names = fieldName.split("\\.");
                        if (names.length == 2) {
                            criterion = setQueryPropertity(criteria, names, filtration.getFieldValue(), filtration.getMatchType(), aliasSet);
                            criterions.add(criterion);
                            continue;
                        }
                    }
                    // criteria.createCriteria("user").add(Restrictions.eq("local", local));
                    criterion = createCriterion(filtration.getFieldName(), filtration.getFieldValue(), filtration.getMatchType());
                    criterions.add(criterion);
                } else {
                    // 包含多个属性需要比较的情况,进行or处理.
                    Disjunction disjunction = Restrictions.disjunction();
                    for (String filedName : filtration.getFieldNames()) {
                        Object v = filtration.getFieldValue();
                        if (filedName.contains(".") && !(v instanceof Integer)) {
                            String[] names = filedName.split("\\.");
                            if (names.length == 2) {
                                criterion = setQueryPropertity(criteria, names, filtration.getFieldValue(), filtration.getMatchType(), aliasSet);
                                disjunction.add(criterion);
                                continue;
                            }
                        }
                        criterion = createCriterion(filedName, filtration.getFieldValue(), filtration.getMatchType());
                        disjunction.add(criterion);
                    }
                    criterions.add(disjunction);
                }
            }
            for (Criterion criterion : criterions) {
                criteria.add(criterion);
            }
        }
        // criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY); // 去重

        return criteria;
    }

    /**
     * 设置过滤条件到Criteria对象
     */
    public static Criteria setFiltrationParameter(Criteria criteria, List<Filtration> filtrationList) {
        if (filtrationList != null) {
            // Filtration[] filtrations = (Filtration[]) filtrationList.toArray();
            Filtration[] filtrations = new Filtration[filtrationList.size()];
            for (int i = 0; i < filtrationList.size(); i++) {
                filtrations[i] = filtrationList.get(i);
            }
            return setFiltrationParameter(criteria, filtrations);
        } else {
            // criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY); // 去重
            return criteria;
        }
    }

    /**
     * 设置分页参数到Criteria对象
     */
    public static Criteria setParameter(Criteria criteria, PageData<?> pageData) {
        // 第一步：设置查询条件
        setFiltrationParameter(criteria, pageData.getFiltrations());
        // 第二步：读取记录总数
        if (pageData.getPagination().isReadTotalCount()) {
            long totalCount = countCriteriaResult(criteria);
            pageData.getPagination().setTotalCount(totalCount);
        }

        // 第三步：设置查询范围
        criteria.setFirstResult(pageData.getPagination().getCurrentlyPageFirstResoultIndex());
        criteria.setMaxResults(pageData.getPagination().getPageSize());

        // 排序条件
        setCompositorParameter(criteria, pageData.getCompositor());

        return criteria;
    }
}
